<!doctype html>
<html itemscope itemtype="http://schema.org/WebPage" lang="zh-cn" class="no-js">
  <head>
    <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Hugo 0.121.1">
<link rel="canonical" type="text/html" href="/docs/contribution-guidelines/runtime/">
<meta name="robots" content="noindex, nofollow">


<link rel="shortcut icon" href="/favicons/favicon.ico" >
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon-180x180.png" sizes="180x180">
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicons/android-36x36.png" sizes="36x36">
<link rel="icon" type="image/png" href="/favicons/android-48x48.png" sizes="48x48">
<link rel="icon" type="image/png" href="/favicons/android-72x72.png" sizes="72x72">
<link rel="icon" type="image/png" href="/favicons/android-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicons/android-144x144.png" sizes="144x144">
<link rel="icon" type="image/png" href="/favicons/android-192x192.png" sizes="192x192">

<title>多模块运行时适配或最佳实践 | SOFAServerless</title>
<meta name="description" content="">
<meta property="og:title" content="多模块运行时适配或最佳实践" />
<meta property="og:description" content="" />
<meta property="og:type" content="website" />
<meta property="og:url" content="/docs/contribution-guidelines/runtime/" />

<meta itemprop="name" content="多模块运行时适配或最佳实践">
<meta itemprop="description" content=""><meta name="twitter:card" content="summary"/><meta name="twitter:title" content="多模块运行时适配或最佳实践"/>
<meta name="twitter:description" content=""/>




<link rel="preload" href="/scss/main.min.526354c4efc1a4747972ed64c3be00f7db62aec5f0a3e6ae42052f97e2675d86.css" as="style">
<link href="/scss/main.min.526354c4efc1a4747972ed64c3be00f7db62aec5f0a3e6ae42052f97e2675d86.css" rel="stylesheet" integrity="">

<script
  src="https://code.jquery.com/jquery-3.6.3.min.js"
  integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ=="
  crossorigin="anonymous"></script>
<link rel="stylesheet" href="/css/prism.css"/>
<link href="/img/logo.svg" rel="icon" type="image/svg">
<link href="/search/pagefind-ui.css" rel="stylesheet">
<script src="/search/pagefind-ui.js" type="text/javascript"></script>

<script>
    window.addEventListener('DOMContentLoaded', (event) => {
        new PagefindUI({ element: ".td-search" });
    });
</script>


<script>
var doNotTrack = false;
if (!doNotTrack) {
	window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
	ga('create', 'G-DZ8Q3F0GZ7', 'auto');
	
	ga('send', 'pageview');
}
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>

<script async src="https://www.googletagmanager.com/gtag/js?id=G-DZ8Q3F0GZ7"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
	window.dataLayer = window.dataLayer || [];
	function gtag(){dataLayer.push(arguments);}
	gtag('js', new Date());
	gtag('config', 'G-DZ8Q3F0GZ7');
}
</script>
  </head>
  <body class="td-section">
    <header>
      <nav class="td-navbar navbar-dark js-navbar-scroll">
<div class="container-fluid flex-column flex-md-row">
  <a class="navbar-brand" href="/"><span class="navbar-brand__logo navbar-logo"><svg width="26" height="29" viewBox="0 0 26 29" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient x1="52.6662558%" y1="95.7312514%" x2="35.7492678%" y2="11.0078657%" id="linearGradient-wxw43fh1xd-1"><stop stop-color="#9822e4" offset="0"/><stop stop-color="#e643fa" offset="86.0585504%"/><stop stop-color="#f876ff" offset="100%"/></linearGradient><path d="M15.9275871 1.54432572 15.936122 1.5594269 15.9445709 1.57457632 5.01248933 7.79418913 7.699 9.304l8.8412359-4.72420311.8668497-.49490764C17.4171462 4.0791454 17.4272394 4.07345885 17.4373647 4.06782987c1.474137-.81952243 3.3335154-.28885241 4.1530379 1.18528466L10.875 11.088l2.864 1.609 8.843271-5.6213813c1.1038914-.6292563 2.5021428-.23250173 3.1230817.88617614C25.8984381 8.30965565 25.9998668 8.70204694 25.9998668 9.10116416V19.9436637c0 1.3476373-1.002317 2.484932-2.3392704 2.6542897L22.9421516 22.6889619V10.4257286L20.457252 12.0257475 20.457758 23.6880152c0 1.2665471-1.0267393 2.2932864-2.2932864 2.2932864H17.59115L17.590252 13.8727475 15.106 15.473 15.1067564 27.0588825c0 1.0695936-.855616100000001 1.9366704-1.911072 1.9366704C12.8696714 28.9955529 12.5490782 28.9110348 12.2643947 28.7500365L1.63270516 23.3171586C.159863707 22.5645249-.423980046 20.7604209.328653641 19.2875794L.372104915 19.2057013 12.0490412 25.0797183 12.049 22.086 1.98952173 16.7675786C.51279311 15.9867912-.0513787599 14.1567129.729408593 12.6799843L.744416289 12.6519572.759717752 12.6240895 12.049 18.593 12.0490412 15.2566254 1.7391964 9.45898637C1.44132502 9.2850324 1.19527431 9.0328708 1.02697438 8.72907568c-.516688418-.93266477-.189466545-2.11320819.7308705-2.63681754C6.19445786 3.56812633 9.52191759 1.67502747 11.7402241.412961569L11.7628911.400065593c1.4660277-.834070345 3.3306257-.321767634 4.164696 1.144260127z" id="path-wxw43fh1xd-2"/><linearGradient x1="50.6099966%" y1="31.6743333%" x2="50.2419846%" y2="73.0702907%" id="linearGradient-wxw43fh1xd-4"><stop stop-color="#8200b5" offset="0"/><stop stop-color="#8200b5" stop-opacity="0" offset="100%"/></linearGradient><linearGradient x1="50.6028666%" y1="31.6743333%" x2="50.2391561%" y2="73.0702907%" id="linearGradient-wxw43fh1xd-5"><stop stop-color="#8200b5" offset="0"/><stop stop-color="#8200b5" stop-opacity="0" offset="100%"/></linearGradient><linearGradient x1="90.8235257%" y1="50%" x2="3.24041867%" y2="52.9415572%" id="linearGradient-wxw43fh1xd-6"><stop stop-color="#dd3ff7" stop-opacity="0" offset="0"/><stop stop-color="#ca23e4" offset="100%"/></linearGradient><linearGradient x1="74.3272776%" y1="35.8586031%" x2="19.6895996%" y2="19.3309621%" id="linearGradient-wxw43fh1xd-7"><stop stop-color="#b800d1" stop-opacity="0" offset="0"/><stop stop-color="#ae00c5" offset="100%"/></linearGradient><linearGradient x1="97.0070676%" y1="71.8003287%" x2="11.3661868%" y2="30.5044309%" id="linearGradient-wxw43fh1xd-8"><stop stop-color="#940ec7" offset="0"/><stop stop-color="#c435f0" stop-opacity="0" offset="100%"/></linearGradient></defs><g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="首页" transform="translate(-24.000000, -18.000000)"><g id="编组-4" transform="translate(24.000133, 16.000000)"><g id="路径-6-copy-2" transform="translate(0.000000, 2.004447)"><mask id="mask-wxw43fh1xd-3" fill="#fff"><use xlink:href="#path-wxw43fh1xd-2"/></mask><use id="Mask" fill="url(#linearGradient-wxw43fh1xd-1)" fill-rule="nonzero" xlink:href="#path-wxw43fh1xd-2"/><path d="M12.0490412 15.2684078 13.03943 15.5167092C14.3279076 16.0478608 15.1686754 17.3038563 15.1686754 18.6975193V18.7421449v9.047164l-3.1196342-1.4070083V15.2684078z" id="Path-114-Copy-2" fill="url(#linearGradient-wxw43fh1xd-4)" opacity=".69047619" mask="url(#mask-wxw43fh1xd-3)"/><path d="M22.9421516 10.4227424 23.8594661 10.2542707C24.8889759 10.219744 25.7876765 10.946368 25.9694916 11.9602841L26.0763097 12.5559692 26.0617858 22.9064645l-3.1196342-1.4070082V10.4227424z" id="Path-114-Copy-2" fill="url(#linearGradient-wxw43fh1xd-5)" opacity=".69047619" mask="url(#mask-wxw43fh1xd-3)"/><polygon id="Path-41" fill="url(#linearGradient-wxw43fh1xd-6)" mask="url(#mask-wxw43fh1xd-3)" points="7.6326499 9.31647968 11.9297751 6.77228295 14.9765833 9.31647968 10.7681311 11.1500753"/><path d="M6.16852974 12.7326226C8.05429918 13.6181711 7.86496926 14.83948 8.18831704 15.006813 8.70089861 15.2720748 9.14143095 17.098905 9.86115602 17.1695013 10.6949216 17.2512836 11.6478081 10.8552079 10.3936906 10.4556578 9.48069361 10.1647855 7.32719679 9.49121213 3.93320016 8.43493769c-.63204609 1.86851711.11306377 3.30107871 2.23532958 4.29768491z" fill="url(#linearGradient-wxw43fh1xd-7)" mask="url(#mask-wxw43fh1xd-3)" transform="translate(7.357416, 12.802608) rotate(-347.000000) translate(-7.357416, -12.802608)"/><polygon id="Path-43" fill="url(#linearGradient-wxw43fh1xd-8)" mask="url(#mask-wxw43fh1xd-3)" points="12.0699788 18.5615749 6.27140769 15.1359473 5.30588708 19.0963147 12.0699788 22.1120507"/></g></g></g></g></svg></span><span class="navbar-brand__name">SOFAServerless</span></a>
  <div class="td-navbar-nav-scroll ms-md-auto" id="main_navbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" href="/home/"><span>首页</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link active" href="/docs/"><span>产品文档</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/blog/"><span>最新信息</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/community/"><span>参与社区</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/user-cases/"><span>用户案例</span></a>
      </li>
      <li class="nav-item dropdown d-none d-lg-block">
        <div class="dropdown">
  <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">中文</a>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="/no/"></a></li>
    </ul>
</div></li>
      </ul>
  </div>
  <div class="d-none d-lg-block">
    <div class="td-search">
  <div class="td-search__icon"></div>
  <input type="search" class="td-search__input form-control td-search-input" placeholder="站内搜索…" aria-label="站内搜索…" autocomplete="off">
</div>

  </div>
</div>
</nav>
    </header>
    <div class="container-fluid td-outer">
      <div class="td-main">
        <div class="row flex-xl-nowrap">
          <main class="col-12 col-md-9 col-xl-8 ps-md-5" role="main">
            




<div class="td-content">
<div class="pageinfo pageinfo-primary d-print-none">
<p>
这是本节的多页打印视图。
<a href="#" onclick="print();return false;">点击此处打印</a>.
</p><p>
<a href="/docs/contribution-guidelines/runtime/">返回本页常规视图</a>.
</p>
</div>



<h1 class="title">多模块运行时适配或最佳实践</h1>





    <ul>
    
  
  
  
  

  
    
    
	
<li>1: <a href="#pg-e8336cb2ec84a57eea63b09a2d517063">log4j2 的多模块化适配</a></li>


    
  
    
    
	
<li>2: <a href="#pg-fce299658e201c9a561eaab5974c0199">ehcache 的多模块化最佳实践</a></li>


    
  

    </ul>


<div class="content">
      
</div>
</div>


  
  
  
  

  
  

  
    
    
	
    

<div class="td-content" style="">
    
	<h1 id="pg-e8336cb2ec84a57eea63b09a2d517063">1 - log4j2 的多模块化适配</h1>
    
	<h2 id="为什么需要做适配">为什么需要做适配</h2>
<p>原生 log4j2 在多模块下，模块没有独立打印的日志目录，统一打印到基座目录里，导致日志和对应的监控无法隔离。这里做适配的目的就是要让模块能有独立的日志目录。</p>
<h2 id="普通应用-log4j2-的初始化">普通应用 log4j2 的初始化</h2>
<p>在 Spring 启动前，log4j2 会使用默认值初始化一次各种 logContext 和 Configuration，然后在 Spring 启动过程中，监听 Spring 事件进行初始化
<code>org.springframework.boot.context.logging.LoggingApplicationListener</code>，这里会调用到 Log4j2LoggingSystem.initialize 方法</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696930949183-9519451c-be76-4d9b-bb6b-28a1b21e7fa7.png" alt=""></p>
<p>该方法会根据 loggerContext 来判断是否已经初始化过了</p>
<blockquote>
<p>这里在多模块下会存在问题一</p>
<p>这里的 getLoggerContext 是根据 org.apache.logging.log4j.LogManager 所在 classLoader 来获取 LoggerContext。根据某个类所在 ClassLoader 来提取 LoggerContext 在多模块化里会存在不稳定，因为模块一些类可以设置为委托给基座加载，所以模块里启动的时候，可能拿到的 LoggerContext 是基座的，导致这里 isAlreadyInitialized 直接返回，导致模块的 log4j2 日志无法进一步根据用户配置文件配置。</p>
</blockquote>
<p>如果没初始化过，则会进入 super.initialize, 这里需要做两部分事情：</p>
<ol>
<li>获取到日志配置文件</li>
<li>解析日志配置文件里的变量值
这两部分在多模块里都可能存在问题，先看下普通应用过程是如何完成这两步的</li>
</ol>
<h3 id="获取日志配置文件">获取日志配置文件</h3>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696931678652-81a19dc2-f618-48b0-add3-d098d3781966.png?x-oss-process=image%2Fresize%2Cw_1500%2Climit_0" alt=""></p>
<p>可以看到是通过 ResourceUtils.getURL 获取的 location 对应日志配置文件的 url，这里通过获取到当前线程上下文 ClassLoader 来获取 URL，这在多模块下没有问题（因为每个模块启动时线程上下文已经是 模块自身的 ClassLoader ）。</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696931908899-f1fac1bb-f365-49f9-81a2-3e2d924c2b7d.png?x-oss-process=image%2Fresize%2Cw_1500%2Climit_0" alt=""></p>
<h3 id="解析日志配置值">解析日志配置值</h3>
<p>配置文件里有一些变量，例如这些变量</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696932148670-d04bde21-e46b-476c-9cf5-53e43cc4dbe2.png" alt=""></p>
<p>这些变量的解析逻辑在 <code>org.apache.logging.log4j.core.lookup.AbstractLookup</code> 的具体实现里，包括</p>
<table>
<thead>
<tr>
<th></th>
<th>变量写法</th>
<th>代码逻辑地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>${bundle:application:logging.file.path}</td>
<td>org.apache.logging.log4j.core.lookup.ResourceBundleLookup</td>
<td>根据 ResourceBundleLookup 所在 ClassLoader 提前到 application.properties, 读取里面的值</td>
</tr>
<tr>
<td>${ctx:logging.file.path}</td>
<td>org.apache.logging.log4j.core.lookup.ContextMapLookup</td>
<td>根据 LoggerContext 上下文 ThreadContex 存储的值来提起，这里需要提前把 applicaiton.properties 的值设置到 ThreadContext 中</td>
</tr>
</tbody>
</table>
<p>根据上面判断通过 bundle 的方式配置在多模块里不可行，因为 ResourceBundleLookup 可能只存在于基座中，导致始终只能拿到基座的 application.properties，导致模块的日志配置路径与基座相同，模块日志都打到基座中。所以需要改造成使用 ContextMapLookup。</p>
<h2 id="预期多模块合并下的日志">预期多模块合并下的日志</h2>
<p>基座与模块都能使用独立的日志配置、配置值，完全独立。但由于上述分析中，存在两处可能导致模块无法正常初始化的逻辑，故这里需要多 log4j2 进行适配。</p>
<h3 id="多模块适配点">多模块适配点</h3>
<ol>
<li>
<p>getLoggerContext() 能拿到模块自身的 LoggerContext
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696938182575-51ce1066-21f0-47bb-8bdb-c3c7d0814ca3.png" alt=""></p>
</li>
<li>
<p>需要调整成使用 ContextMapLookup，从而模块日志能获取到模块应用名，日志能打印到模块目录里</p>
<p>a. 模块启动时将 application.properties 的值设置到 ThreadContext 中
b. 日志配置时，只能使用 ctx:xxx:xxx 的配置方式</p>
</li>
</ol>
<h2 id="模块改造方式">模块改造方式</h2>
<p><a href="https://github.com/sofastack/sofa-serverless/tree/master/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-log4j2/src/main/java/com/alipay/sofa/serverless/log4j2">详细查看源码</a></p>

</div>



    
	
  
    
    
	
    

<div class="td-content" style="page-break-before: always">
    
	<h1 id="pg-fce299658e201c9a561eaab5974c0199">2 - ehcache 的多模块化最佳实践</h1>
    
	<h2 id="为什么需要最佳实践">为什么需要最佳实践</h2>
<p>CacheManager 初始化的时候存在共用 static 变量，多应用使用相同的 ehcache name，导致缓存互相覆盖。</p>
<h2 id="最佳实践的几个要求">最佳实践的几个要求</h2>
<ol>
<li>基座里必须引入 ehcache，模块里复用基座</li>
</ol>
<p>在 springboot 里 ehcache 的初始化需要通过 Spring 里定义的 EhCacheCacheConfiguration 来创建，由于 EhCacheCacheConfiguration 是属于 Spring, Spring 统一放在基座里。
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700202934067-7a0d74b7-b765-4c96-ab95-6189602235b8.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=679&amp;id=u3a86e2ae&amp;originHeight=1358&amp;originWidth=2284&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=801737&amp;status=done&amp;style=none&amp;taskId=ub2119003-e3dd-4276-83a3-bc0a8598185&amp;title=&amp;width=1142" alt="image.png"></p>
<p>这里在初始化的时候，在做 Bean 初始化的条件判断时会走到类的检验，
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700203147758-c2f4f211-27b1-408a-8a59-04b54a0602f3.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=532&amp;id=ea4Xj&amp;originHeight=1064&amp;originWidth=1052&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=607056&amp;status=done&amp;style=none&amp;taskId=u59dc4240-37cd-4a97-8b57-0e71250149b&amp;title=&amp;width=526" alt="image.png">
如果 net.sf.ehcache.CacheManager 是。这里会走到 java native 方法上做判断，从当前类所在的 ClassLoader 里查找 net.sf.ehcache.CacheManager 类，所以基座里必须引入这个依赖，否则会报 ClassNotFound 的错误。
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700203220867-62f2b7be-e853-488c-a6bc-a95c874793f1.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=97&amp;id=u3ca967f5&amp;originHeight=194&amp;originWidth=1798&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=104469&amp;status=done&amp;style=none&amp;taskId=u4957f800-31ee-40b3-bb09-487b9ab16ba&amp;title=&amp;width=899" alt="image.png"></p>
<ol start="2">
<li>模块里将引入的 ehcache 排包掉（scope设置成 provide，或者使用自动瘦身能力）</li>
</ol>
<p>模块使用自己 引入的 ehcache，照理可以避免共用基座 CacheManager 类里的 static 变量，而导致报错的问题。但是实际测试发现，模块安装的时候，在初始化 enCacheCacheManager 时，
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700203897715-c9f97922-b466-4e73-8319-1a0f5ec3cc73.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=211&amp;id=uaa50406f&amp;originHeight=422&amp;originWidth=2048&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=235120&amp;status=done&amp;style=none&amp;taskId=ub3d92b21-fec0-4462-92ad-91449dcea2d&amp;title=&amp;width=1024" alt="image.png">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700203915265-f42253e4-1ff4-4088-a87e-8b6e063540ba.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=107&amp;id=uedd0a010&amp;originHeight=214&amp;originWidth=1258&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=101140&amp;status=done&amp;style=none&amp;taskId=u044240e0-fe55-4f77-b63e-41ebf9eca47&amp;title=&amp;width=629" alt="image.png">
这里在 new 对象时，需要先获得对象所属类的 CacheManager 是基座的 CacheManager。这里也不能讲 CacheManager 由模块 compile 引入，否则会出现一个类由多个不同 ClassLoader 引入导致的问题。
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700212320690-8112f0f7-7ab7-48a7-8d9d-95aa3d49492a.png#clientId=u4cdbd480-e8bb-4&amp;from=paste&amp;height=145&amp;id=ud90248f9&amp;originHeight=290&amp;originWidth=2736&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=294518&amp;status=done&amp;style=none&amp;taskId=ue9c723ea-0a3b-4854-b069-402238e5fcd&amp;title=&amp;width=1368" alt="image.png"></p>
<p>所以结论是，这里需要全部委托给基座加载。</p>
<h2 id="最佳实践的方式">最佳实践的方式</h2>
<ol>
<li>模块 ehcache 排包瘦身委托给基座加载</li>
<li>如果多个模块里有多个相同的 cacheName，需要修改 cacheName 为不同值。</li>
<li>如果不想改代码的方式修改 cache name，可以通过打包插件的方式动态替换 cacheName</li>
</ol>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;groupId&gt;</span>com.google.code.maven-replacer-plugin<span style="color:#204a87;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;artifactId&gt;</span>replacer<span style="color:#204a87;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;version&gt;</span>1.5.3<span style="color:#204a87;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;executions&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8f5902;font-style:italic">&lt;!-- 打包前进行替换 --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;execution&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;phase&gt;</span>prepare-package<span style="color:#204a87;font-weight:bold">&lt;/phase&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;goals&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#204a87;font-weight:bold">&lt;goal&gt;</span>replace<span style="color:#204a87;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;/goals&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;/execution&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;/executions&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8f5902;font-style:italic">&lt;!-- 自动识别到项目target文件夹 --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;basedir&gt;</span>${build.directory}<span style="color:#204a87;font-weight:bold">&lt;/basedir&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8f5902;font-style:italic">&lt;!-- 替换的文件所在目录规则 --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;includes&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;include&gt;</span>classes/j2cache/*.properties<span style="color:#204a87;font-weight:bold">&lt;/include&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;/includes&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;replacements&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;replacement&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#204a87;font-weight:bold">&lt;token&gt;</span>ehcache.ehcache.name=f6-cache<span style="color:#204a87;font-weight:bold">&lt;/token&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#204a87;font-weight:bold">&lt;value&gt;</span>ehcache.ehcache.name=f6-${parent.artifactId}-cache<span style="color:#204a87;font-weight:bold">&lt;/value&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#204a87;font-weight:bold">&lt;/replacement&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#204a87;font-weight:bold">&lt;/replacements&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#204a87;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><ol start="4">
<li>需要把 FactoryBean 的 shared 设置成 false</li>
</ol>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Bean</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span><span style="color:#204a87;font-weight:bold">public</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EhCacheManagerFactoryBean</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">ehCacheManagerFactoryBean</span><span style="color:#000;font-weight:bold">()</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">{</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#000">EhCacheManagerFactoryBean</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">factoryBean</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">new</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EhCacheManagerFactoryBean</span><span style="color:#000;font-weight:bold">();</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#8f5902;font-style:italic">// 需要把 factoryBean 的 share 属性设置成 false</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#000">factoryBean</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">setShared</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic">//        factoryBean.setShared(false);</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#000">factoryBean</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">setCacheManagerName</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#34;biz1EhcacheCacheManager&#34;</span><span style="color:#000;font-weight:bold">);</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#000">factoryBean</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">setConfigLocation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">new</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">ClassPathResource</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#34;ehcache.xml&#34;</span><span style="color:#000;font-weight:bold">));</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">        </span><span style="color:#204a87;font-weight:bold">return</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">factoryBean</span><span style="color:#000;font-weight:bold">;</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span><span style="color:#000;font-weight:bold">}</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><p>否则会进入这段逻辑，初始化 CacheManager 的static 变量 instance. 该变量如果有值，且如果模块里 shared 也是ture 的化，就会重新复用 CacheManager 的 instance，从而拿到基座的 CacheManager, 从而报错。
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700360794825-3f7f4a63-22bc-49ea-81d1-83bd94804087.png#clientId=u2481e0c2-f328-4&amp;from=paste&amp;height=399&amp;id=u7432be71&amp;originHeight=798&amp;originWidth=1596&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=422965&amp;status=done&amp;style=none&amp;taskId=u1e450639-4846-4b6a-9862-bac787ae8e5&amp;title=&amp;width=798" alt="image.png">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1700359643422-7b252689-7e0c-41f3-995e-cbc40726136e.png#clientId=u2481e0c2-f328-4&amp;from=paste&amp;height=161&amp;id=u80efa85e&amp;originHeight=322&amp;originWidth=2426&amp;originalType=binary&amp;ratio=2&amp;rotation=0&amp;showTitle=false&amp;size=339519&amp;status=done&amp;style=none&amp;taskId=u15aeda8f-e089-4bf0-8bc7-e47eff9d2f0&amp;title=&amp;width=1213" alt="image.png"></p>
<h2 id="最佳实践的样例">最佳实践的样例</h2>
<p>样例工程请<a href="https://github.com/sofastack/sofa-serverless/tree/master/samples/springboot-samples/cache/ehcache">参考这里</a></p>

</div>



    
	
  



          </main>
        </div>
      </div>
      <footer class="td-footer row d-print-none">
  <div class="container-fluid">
    <div class="row mx-md-2">
      <div class="col-6 col-sm-4 text-xs-center order-sm-2">
        
        
        
<ul class="td-footer__links-list">
  
  <li class="td-footer__links-item" data-bs-toggle="tooltip" title="通过社区交流群" aria-label="通过社区交流群">
    <a target="_blank" rel="noopener" href="/docs/contribution-guidelines/communication-channel/" aria-label="通过社区交流群">
      <i class="fab fa-twitter"></i>
    </a>
  </li>
  
</ul>

        
        
      </div>
      <div class="col-6 col-sm-4 text-end text-xs-center order-sm-3">
        
        
        
<ul class="td-footer__links-list">
  
  <li class="td-footer__links-item" data-bs-toggle="tooltip" title="看这里" aria-label="看这里">
    <a target="_blank" rel="noopener" href="/docs/contribution-guidelines/contribution/first-pr/" aria-label="看这里">
      <i class="fab fa-github"></i>
    </a>
  </li>
  
</ul>

        
        
      </div>
      <div class="td-footer__copyright-etc col-12 col-sm-4 text-center py-2 order-sm-2">
        <span>&copy; 2023 SOFAServerless 开源社区 保留所有权利</span>
        
        
      </div>
    </div>
  </div>
</footer>
    </div>
    
  <script src="/js/main.min.1eb4262674b2d02aa8d18559fef13b166dbdfa627fd0a495c66e11577c026aa3.js" integrity="sha256-HrQmJnSy0Cqo0YVZ/vE7Fm29&#43;mJ/0KSVxm4RV3wCaqM=" crossorigin="anonymous"></script>
<script src='/js/prism.js'></script>
<script src='/js/tabpane-persist.js'></script>

  </body>
</html>
